package com.flyxteam.commons.dao.mybatis.core.sqlbuilder;

import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.flyxteam.commons.dao.mybatis.api.condition.Condition;
import com.flyxteam.commons.dao.mybatis.api.exception.MybatisArgumentException;
import com.flyxteam.commons.dao.mybatis.core.constants.ConditionSign;

/**
 * 条件SQL构建器
 * 
 * @author yuzh
 *
 */
public abstract class ConditionSqlBuilder extends AbstractSqlBuilder {
	
	/** 条件字段 */
	protected List<Condition> conditionColumns;

	/**
	 * 构建条件sql
	 * 
	 * @param conditionList
	 * @return
	 */
	protected String buildConditionSqlString() {
		if(conditionColumns == null || conditionColumns.isEmpty()) {
			return "";
		}
		StringBuilder conditionSql = new StringBuilder("where ");
		Condition condition = null;
		for (int i = 0, size = conditionColumns.size(); i < size; i++) {
			condition = conditionColumns.get(i);
			// 验证合法性
			validateConditionBean(condition);
			// 拼装and|or
			if (i > 0) {
				conditionSql.append(condition.getLogicSign().getValue()).append(" ");
			}
			// 拼装sql条件
			buildCondition(conditionSql, condition, i);
		}
		return conditionSql.toString();
	}

	/**
	 * 验证sql条件合法性
	 * 
	 * @param condition
	 */
	private void validateConditionBean(Condition condition) {
		if (StringUtils.isBlank(condition.getColumnName())) {
			throw new MybatisArgumentException("字段名不能为空");
		}
		if (condition.getConditionSign() == null) {
			throw new MybatisArgumentException("条件符号不能为空");
		}
		if (condition.getConditionSign() != ConditionSign.IS_NULL
				&& condition.getConditionSign() != ConditionSign.IS_NOT_NULL) {
			if (condition.getColumnValue() == null) {
				throw new MybatisArgumentException("字段值不能为空");
			}
		}
		if (condition.getLogicSign() == null) {
			throw new MybatisArgumentException("条件逻辑符号不能为空");
		}
	}

	/**
	 * 拼装sql条件
	 * 
	 * @param conditionSql
	 * @param condition
	 * @param index
	 */
	@SuppressWarnings("unchecked")
	private void buildCondition(StringBuilder conditionSql, Condition condition, int index) {
		switch (condition.getConditionSign()) {
		case EQ: // =
			validateSingleValue(condition.getColumnValue());
			buildSingleCondition(conditionSql, condition, index, "=");
			buildSqlParam(condition, index);
			break;
		case GT: // >
			validateSingleValue(condition.getColumnValue());
			buildSingleCondition(conditionSql, condition, index, ">");
			buildSqlParam(condition, index);
			break;
		case GTE: // >=
			validateSingleValue(condition.getColumnValue());
			buildSingleCondition(conditionSql, condition, index, ">=");
			buildSqlParam(condition, index);
			break;
		case LT: // <
			validateSingleValue(condition.getColumnValue());
			buildSingleCondition(conditionSql, condition, index, "<");
			buildSqlParam(condition, index);
			break;
		case LTE: // <=
			validateSingleValue(condition.getColumnValue());
			buildSingleCondition(conditionSql, condition, index, "<=");
			buildSqlParam(condition, index);
			break;
		case LIKE: // like %xxx%
			validateSingleValue(condition.getColumnValue());
			conditionSql.append(condition.getColumnName()).append(" like ").append("concat('%', ").append("#{").append(condition.getColumnName())
					.append("_condition_").append(index).append("} ").append(", '%') ");
			buildSqlParam(condition, index);
			break;
		case LIKE_PRE: // like %xxx
			validateSingleValue(condition.getColumnValue());
			conditionSql.append(condition.getColumnName()).append(" like ").append("concat('%', ").append("#{").append(condition.getColumnName())
					.append("_condition_").append(index).append("} ").append(") ");
			buildSqlParam(condition, index);
			break;
		case LIKE_FIX: // like xxx%
			validateSingleValue(condition.getColumnValue());
			conditionSql.append(condition.getColumnName()).append(" like ").append("concat(").append("#{").append(condition.getColumnName())
					.append("_condition_").append(index).append("} ").append(", '%') ");
			buildSqlParam(condition, index);
			break;
		case IS_NULL: // is null
			conditionSql.append(condition.getColumnName()).append(" is null ");
			buildSqlParam(condition, index);
			break;
		case IS_NOT_NULL: // is not null
			conditionSql.append(condition.getColumnName()).append(" is not null ");
			buildSqlParam(condition, index);
			break;
		case BETWEEN: // between xxx in xxx
			validateMultiValue(condition.getColumnValue());
			List<Object> columnValue = (List<Object>) condition.getColumnValue();
			conditionSql.append(condition.getColumnName()).append(" between ").append("#{").append(condition.getColumnName())
					.append("_condition_").append(index).append("_0").append("} ").append(" and ").append("#{").append(condition.getColumnName())
					.append("_condition_").append(index).append("_1").append("} ");
			sqlParam.put(condition.getColumnName() + "_condition_" + index + "_0", columnValue.get(0));
			sqlParam.put(condition.getColumnName() + "_condition_" + index + "_1", columnValue.get(1));
			break;
		case IN: // in(xxx, xxx, xxx ...)
			validateMultiValue(condition.getColumnValue());
			List<Object> columnMultiValue = (List<Object>) condition.getColumnValue();
			conditionSql.append(condition.getColumnName()).append(" in ").append("(");
			for (int i = 0, size = columnMultiValue.size(); i < size; i++) {
				conditionSql.append("#{").append(condition.getColumnName()).append("_condition_").append(index).append("_").append(i).append("} ");
				sqlParam.put(condition.getColumnName() + "_condition_" + index + "_" + i, columnMultiValue.get(i));
				if((i + 1) < size) {
					conditionSql.append(", ");
				}
			}
			conditionSql.append(") ");
			break;
		default:
			break;
		}
	}
	
	private void validateSingleValue(Object o) {
		if(o instanceof Collection) {
			throw new MybatisArgumentException("参数错误，不接受集合类条件参数");
		}
	}
	
	private void validateMultiValue(Object o) {
		if(!(o instanceof Collection)) {
			throw new MybatisArgumentException("参数错误，只接受集合类条件参数");
		}
	}

	private void buildSingleCondition(StringBuilder conditionSql, Condition condition, int index,
			String conditionSign) {
		conditionSql.append(condition.getColumnName()).append(" ").append(conditionSign).append(" ").append("#{")
				.append(condition.getColumnName()).append("_condition_").append(index).append("} ");
	}

	/**
	 * 拼装sql参数
	 * 
	 * @param condition
	 * @param index
	 */
	private void buildSqlParam(Condition condition, int index) {
		sqlParam.put(condition.getColumnName() + "_condition_" + index, condition.getColumnValue());
	}

}
